package org.finesys.common.security.core.jackson;


import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.finesys.common.security.authentication.password.OAuth2ResourceOwnerPasswordAuthenticationToken;
import org.finesys.common.security.core.util.OAuth2JacksonUtils;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.oauth2.core.AuthorizationGrantType;

import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.MissingNode;

class OAuth2ResourceOwnerPasswordAuthenticationTokenDeserializer extends JsonDeserializer<OAuth2ResourceOwnerPasswordAuthenticationToken> {
    private static final TypeReference<List<GrantedAuthority>> GRANTED_AUTHORITY_LIST = new TypeReference<List<GrantedAuthority>>() {
    };
    private static final TypeReference<Object> OBJECT = new TypeReference<Object>() {
    };

    OAuth2ResourceOwnerPasswordAuthenticationTokenDeserializer() {
    }

    public OAuth2ResourceOwnerPasswordAuthenticationToken deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException {
        ObjectMapper mapper = (ObjectMapper) jp.getCodec();
        JsonNode jsonNode = (JsonNode) mapper.readTree(jp);
        Boolean authenticated = this.readJsonNode(jsonNode, "authenticated").asBoolean();

        String grantType = this.readJsonNode(jsonNode, "authorizationGrantType").asText("");
        AuthorizationGrantType authorizationGrantType = new AuthorizationGrantType(grantType);
        JsonNode authenticationNode = this.readJsonNode(jsonNode, "authentication");
        Authentication authentication = OAuth2JacksonUtils.jsonNodeToAuthentication(authenticationNode);
        JsonNode scopesNode = this.readJsonNode(jsonNode, "scopes");
        Set<String> scopes = OAuth2JacksonUtils.jsonNodeToSet(scopesNode);
        JsonNode additionalParamsNode = this.readJsonNode(jsonNode, "additionalParams");
        Map<String, Object> additionalParams = additionalParamsNode.deepCopy();

        OAuth2ResourceOwnerPasswordAuthenticationToken token = new OAuth2ResourceOwnerPasswordAuthenticationToken(authorizationGrantType, authentication, scopes, additionalParams);
        token.setAuthenticated(authenticated);
        JsonNode detailsNode = this.readJsonNode(jsonNode, "details");
        if (!detailsNode.isNull() && !detailsNode.isMissingNode()) {
            Object details = mapper.readValue(detailsNode.toString(), OBJECT);
            token.setDetails(details);
        } else {
            token.setDetails((Object) null);
        }

        return token;
    }

    private Object getCredentials(JsonNode credentialsNode) {
        return !credentialsNode.isNull() && !credentialsNode.isMissingNode() ? credentialsNode.asText() : null;
    }

    private Object getPrincipal(ObjectMapper mapper, JsonNode principalNode) throws IOException, JsonParseException, JsonMappingException {
        return principalNode.isObject() ? mapper.readValue(principalNode.traverse(mapper), Object.class) : principalNode.asText();
    }

    private JsonNode readJsonNode(JsonNode jsonNode, String field) {
        return (JsonNode) (jsonNode.has(field) ? jsonNode.get(field) : MissingNode.getInstance());
    }
}